% render "layouts/guides.html" do

% Origen.load_target('default')
% dut.timeset(:func) do |t|
%   t.period_in_ns = 10
% end
% tester.set_timeset(:func)

A common workflow is to have a tester timing file defined and managed outside of Origen, in
which case the main concern within your Origen source code is simply to refer to required
timeset(s) by name.

However, Origen does also support a more complex timing definition where waveforms
can be defined and mastered in Origen source code. That is discussed later on in the 
[Complex Timing](<%= path "guides/pattern/timing/#Complex_Timing" %>) section.

### Simple Timing

This guide gives an overview of some of the most common timing related
methods, but to get a complete overview of what is available consult
the [OrigenTesters::Timing API](http://origen-sdk.org/testers/api/OrigenTesters/Timing.html).

A timeset declaration is used to provide information to Origen about what
timeset to use for future test cycles and what period of time each
cycle represents.

Normally this would be initialized within the startup method before generating any
vectors as shown below:

~~~ruby
class MySoCController
  include Origen::Controller

  def startup(options)
    $tester.set_timeset("mode_entry", 40)
  end
end
~~~

The first argument is the name of the timeset, this should correspond to
how the timeset will be named within the test program, and the second argument
is the cycle period in nano-seconds.

This method also accepts a block in which case the contained vectors will generate
with the supplied timeset and subsequent vectors will return to the previous timeset
automatically. 

~~~ruby
$tester.set_timeset("bist_50mhz", 20) do
  # Any cycles generated in here will use 20ns for the period
end
~~~

The arguments can also be supplied as a single array, or not at all. In the latter case
the existing timeset will simply be preserved. This is useful if you have timesets that
can be conditionally set based on the target.

~~~ruby
# Target 1
$dut.readout_timeset = ["readout", 120]
# Target 2
$dut.readout_timeset = false

# This code is compatible with both targets, in the first case the timeset will switch
# over, in the second case the existing timeset will be preserved.
$tester.set_timeset($dut.readout_timeset) do
  # Generate readout vectors...
end
~~~

#### Creating a Timing Set

Currently the creation of the timing set for a given test platform must be
done independently of Origen, however adding an Origen API for this is on the roadmap.

#### Waiting

All $tester models will support the following API to generate wait states
in the test patterns.

Wait for specific number of cycles:

~~~ruby
$tester.wait(cycles: 1000)
~~~

Wait for a period of time:

~~~ruby
$tester.wait(time_in_us: 500)
$tester.wait(time_in_ms: 10)
~~~

A shorthand for the above cases is available:

~~~ruby
500.us!  # Wait for 500us
10.ms!   # Wait for 10ms
~~~

Multiple times specified in different units will be added together, this can be useful
if the delay is based on a complex calculation:

~~~ruby
# Wait for 500us + 100 cycles
$tester.wait(time_in_us: 500, cycles: 100)
~~~

#### Waiting for an Event

All testers provide an API for generating match loops, these can be used to
make the pattern wait dynamically for a pin-based or even a register-based
event.

To do this enable the <code>:match</code> option and supply a block, within the block
generate the vectors that will test if the condition has been met.
Any time options passed in will be applied as a timeout, i.e. the maximum time to
wait for the required condition to resolve.

Here are some examples:

~~~ruby
# Wait for up to 1 second for the done bit to be set
$tester.wait(match: true, time_in_s: 1) do
  reg(:status_reg).bit(:done).read!(1)
end

# Wait for up to 1 second for the done pin to be set
$tester.wait(match: true, time_in_s: 1) do
  pin(:done).assert!(1)
end
~~~

### Complex Timing

#### Defining Timesets

A wave object defines the waveform that should be applied to a pin, and each pin will
have two wave objects assigned to it - one for drive cycles and one for cycles where the
pin is being read/compared.

A timeset object is used to collect the wave objects for all pins. Multiple timesets can
be defined and therefore the waveforms being applied to the pins can be changed by changing
the timeset.
The API to change the timeset is the same as that already discussed above:

~~~ruby
tester.set_timeset('func', 100)
~~~

The period of the timeset can be referenced in the wave definitions to make the waveforms
keep the same shape with different period settings.

Here is the most basic timeset definition, this will give all pins a default waveform which
drives for the whole period on drive cycles, and strobes at 50% of compare cycles:

~~~ruby
# Simple definition, all pins have default waves
add_timeset :t1
~~~

Here is a more complex definition, which changes the default compare for all pins to be at
25% of the period, and which adds a unique drive wave to the :tck pin to create a clock:

~~~ruby
# Complex definition, defines an alternative default compare wave and specific timing for :tck
timeset :func do |t|
  t.compare_wave do |w|
    w.compare :data, at: "period / 4"
  end

  t.drive_wave :tck do |w|
     w.drive :data, at: 0
     w.drive 0, at: 25
     w.dont_care at: "period - 10"  # Just to show that dont_care can be used
  end
end
~~~

Pin groups can also be referenced in the timeset definition, here to add common drive timing
to all pins in :gpio, and with a special compare waveform for :gpio5 only:

~~~ruby
# Another timeset to show the wave assignment to pin groups
timeset :t2 do |t|
  t.compare_wave :gpio5 do |w|
    w.compare :data, at: 100
  end

  t.drive_wave :gpio do |w|
    w.drive :data, at: 200
  end
end
~~~

Note that the API currently only supports edge compares, i.e. window compares cannot be defined.

Additional drive and compare waves can be defined for any pin by giving them a code which can
be used to select the given waveform in a pattern - i.e. [the approach used by the 
V93K tester to select from multiple available waveforms.](<%= path "guides/pattern/v93k" %>)

Here is an example with a single pulse waveform defined for a clk pin which will be applied
when the pin is driven to <code>1</code>, and a double pulse waveform that will be applied when the pin
is drive to <code>T</code>:

~~~ruby
timeset :func do |t|
  t.drive_wave :tck do |w|
    w.drive 1, at: 0
    w.drive 0, at: 20
  end

  t.drive_wave :tck, code: 'T' do |w|
    w.drive 1, at: 0
    w.drive 0, at: 10
    w.drive 1, at: 20
    w.drive 0, at: 30
  end
end
~~~

Complex waveforms also support an initial, or default, `period_in_ns`, allowing
`set_timeset` to be used without providing a `period_in_ns`:

~~~ruby
dut.timeset :func do |t|
  t.period_in_ns = 10
end
  
tester.set_timeset(:func)
tester.timeset.period_in_ns
  #=> <%= tester.timeset.period_in_ns %>

dut.timesets[:func].period_in_ns = 11
% dut.timesets[:func].period_in_ns = 11
tester.timeset.period_in_ns
  #=> <%= tester.timeset.period_in_ns %>
~~~

<!-- This was originally planned, but causes some examples in Origen to fail.
This `period_in_ns` can still changed prior to pattern execution, but the defining
timeset can lock this value, disallowing other parts of the application or
plugins to change the `period_in_ns`:

~~~ruby
timeset :func do |t|
  t.period_in_ns = 10
  t.lock!
end
  
tester.set_timeset(:func)
tester.timeset.period_in_ns
  #=> 10

timesets[:func].period_in_ns = 11
  #=> OrigenTester::Timing::InvalidModification
~~~
-->

#### Consuming Timeset Data

##### Waveform Details

The currently selected timeset can be retrieved via the following methods which are aliases:

~~~ruby
dut.timeset               # => instance of OrigenTesters::Timing::Timeset
dut.current_timeset
~~~

Even though this returns a `OrigenTesters::Timing::Timeset` object, its linked
back to the corresponding `Origen::Pins::Timing::Timeset` object underneath. To
access it directly:

~~~ruby
dut.timeset.dut_timeset    #=> instance of Origen::Pins::Timing::Timeset

tester.timeset.dut_timeset #=> instance of Origen::Pins::Timing::Timeset
~~~

However, this is generally not needed, as anything that the `OrigenTesters::Timing::Timeset`
cannot do is given to the corresponding `Origen::Pins::Timing::Timeset` instance.

Using the plural returns a hash containing all timesets, thereby allowing access to specific timesets
regardless of the current selection:

~~~ruby
dut.timesets          # => Hash
dut.timesets[:func]   # => instance of Origen::Pins::Timing::Timeset
# This will also work:
dut.timeset(:func)   
~~~

Each timeset stores the waves in two arrays, one for drive waves and another for the compare waves:

~~~ruby
dut.timeset(:func).drive_waves    # => Array containing instances of Origen::Pins::Timing::Wave
dut.timeset(:func).compare_waves
~~~

Each wave object has an events array, and the pins assigned to it can also be retrieved:

~~~ruby
drive_wave = dut.timeset(:func).drive_waves.first
compare_wave = dut.timeset(:func).compare_waves.first

drive_wave.events    # => [[0, :data], [50, 0], [75, :x]]
drive_wave.pins      # => Array of Origen::Pins::Pin instances

compare_wave.events  # => [["period / 2", :data]]
~~~

The method evaluated_events can be used to get the events with the formulas evaluated based on the
current period:

~~~ruby
compare_wave.events            # => [["period / 2", :data]]
compare_wave.evaluated_events  # => [[50, :data]]
~~~

The waves assigned to a given pin by the current timeset can also be accessed via the pin API:

~~~ruby
dut.pin(:tms).compare_wave.events[0]   # => ["period / 2", :data]
~~~

The waveform associated with a particular code can be accessed as follows:

~~~ruby
# The wave for driving 0 and 1 is returned by default, these are all aliases:
dut.pin(:clk).drive_wave.events       # => [[0, 1], [20, 0]]
dut.pin(:clk).drive_wave(0).events    # => [[0, 1], [20, 0]]
dut.pin(:clk).drive_wave(1).events    # => [[0, 1], [20, 0]]

dut.pin(:clk).drive_wave('T').events  # => [[0, 1], [10, 0], [20, 1], [30, 0]]
~~~

##### Timeset Details

You can retrieve the same `current period` information from this scope as from
the `tester.timeset` scope:

~~~ruby
tester.set_timeset(:test, 10)
% tester.set_timeset(:test, 10)

tester.current_period
  #=> <%= tester.current_period %>

dut.current_timeset_period
  #=> <%= dut.current_timeset_period %>
  
dut.current_period_in_ns
  #=> <%= dut.current_period_in_ns %>

dut.current_period_in_seconds
  #=> <%= dut.current_period_in_seconds %>
~~~

### Timing During Execution

You can freely swap between timesets during pattern execution. For example,
a sequence resembling below is perfectly valid:

~~~ruby
# Start with an initial timeset for pattern startup
tester.set_timeset("timing_for_startup", 100)
tester.cycle
# ...

# Switch to a timeset for ramloading
tester.set_timeset("timing_for_ramload", 200)
tester.cycle
# ...

# Switch to a timeset for actual execution
tester.set_timeset("timing_for_execution", 100)
tester.cycle
# ...
~~~

The period for that timeset will be remembered, allowing simpler timeset switches
back to existing ones:

~~~ruby
# Execute using timing for SWD
% tester.set_timeset("timing_for_swd", 40)
tester.set_timeset("timing_for_swd", 40)
tester.timeset.period_in_ns
  #=> <%= tester.timeset.period_in_ns %>
# ...

# Execute using timing for an APB protocol
% tester.set_timeset("timing_for_apb", 80)
tester.set_timeset("timing_for_apb", 80)
tester.timeset.period_in_ns
  #=> <%= tester.timeset.period_in_ns %>
# ...

# Switch back to using SWD
% tester.set_timeset("timing_for_swd")
tester.set_timeset("timing_for_swd")
tester.timeset.period_in_ns
  #=> <%= tester.timeset.period_in_ns %>
# ...

# Switch back to using APB
% tester.set_timeset("timing_for_apb")
tester.set_timeset("timing_for_apb")
tester.timeset.period_in_ns
  #=> <%= tester.timeset.period_in_ns %>
# ...

# End the pattern using SWD
% tester.set_timeset("timing_for_swd")
tester.set_timeset("timing_for_swd")
tester.timeset.period_in_ns
  #=> <%= tester.timeset.period_in_ns %>
# ...
~~~

<!--
Notice that the `period_in_ns` is only defined once per timeset. Swiching
the `period_in_ns` of a timeset _during_ execution is explicitly blocked
by `OrigenTesters`:

~~~ruby
tester.set_timeset(:func, 10)

tester.cycle
tester.set_timeset(:func, 11)
  #=> OrigenTester::Timing::InvalidModification
~~~

In this context, _during execution_ is defined as after a vector has been
generated with the timeset in question; in other words, after the tester
has been cycled. Up to the point of cycling though, timeset periods can be
freely modified:

~~~ruby
tester.set_timeset(:func, 10)
tester.timeset.period_in_ns
  #=> 10

tester.timeset.period_in_ns = 11
tester.timeset.period_in_ns
  #=> 11

tester.set_timeset(:func, 12)
tester.timeset.period_in_ns
  #=> 12

tester.cycle
tester.timeset.period_in_ns = 13
  #=> OrigenTester::Timing::InvalidModification
~~~
-->
The `#cycled?` method to denotes if the timeset has been cycled or not:

~~~ruby
tester.set_timeset(:cycle_test, 10)
% tester.set_timeset(:cycle_test, 10)
tester.timeset.name
  #=> "<%= tester.timeset.name %>"
tester.timeset.cycled?
  #=> <%= tester.timeset.cycled? %>

tester.cycle
tester.timeset.cycled?
% tester.cycle
  #=> <%= tester.timeset.cycled? %>

tester.set_timeset(:cycle_test_2, 11)
tester.timeset.cycled?
% tester.set_timeset(:cycle_test_2, 11)
  #=> <%= tester.timeset.cycled? %>

tester.timesets[:cycle_test].cycled?
  #=> <%= tester.timesets[:cycle_test].cycled? %>
tester.timesets[:cycle_test_2].cycled?
  #=> <%= tester.timesets[:cycle_test_2].cycled? %>
~~~

% end
